import requests
import json
import base64
import time
from datetime import datetime, timedelta
import traceback
import xml.etree.ElementTree as ET

class WalmartAPI:
    def __init__(self, client_id, client_secret):
        self.client_id = client_id
        self.client_secret = client_secret
        self.token = None
        self.token_expiry = None
        self.base_url = "https://marketplace.walmartapis.com/v3"
        
    def get_token(self):
        """获取OAuth 2.0访问令牌，如果已存在有效令牌则直接返回"""
        if self.token and self.token_expiry and datetime.now() < self.token_expiry:
            return self.token
        
        correlation_id = datetime.now().strftime("%Y%m%dT%H%M%SZ")
        auth_url = f"{self.base_url}/token"
        
        # 确保客户端ID和密钥不含前后空格
        client_id = self.client_id.strip()
        client_secret = self.client_secret.strip()
        
        # 按照Walmart API规范创建Basic认证头
        credentials = f"{client_id}:{client_secret}"
        encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
        
        # OAuth 2.0认证头
        headers = {
            "Authorization": f"Basic {encoded_credentials}",
            "WM_SVC.NAME": "Walmart Marketplace",
            "WM_QOS.CORRELATION_ID": correlation_id,
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded"
        }
        
        # 标准OAuth2形式的请求体
        data = {
            "grant_type": "client_credentials"
        }
        
        try:
            # 打印请求信息（不包含敏感信息）
            print(f"请求令牌URL: {auth_url}")
            print(f"使用Client ID的前8个字符: {client_id[:8] if len(client_id) > 8 else '***'}...")
            
            response = requests.post(auth_url, headers=headers, data=data)
            
            # 打印响应信息以便调试
            print(f"认证响应状态码: {response.status_code}")
            if response.text:
                print(f"认证响应内容: {response.text[:200]}...")  # 只打印前200个字符
            
            # 尝试识别常见的错误类型并给出建议
            if response.status_code == 400:
                error_text = response.text
                if "INVALID_REQUEST_HEADER" in error_text:
                    print("建议: 认证头格式可能有误，请确保Client ID和Secret完全正确且无额外字符")
                elif "unauthorized_client" in error_text:
                    print("建议: 认证失败，客户端ID或密钥可能无效")
            
            response.raise_for_status()
            
            # 尝试解析为JSON格式
            try:
                token_data = response.json()
                self.token = token_data.get('access_token')
                expires_in = token_data.get('expires_in', 900)  # 默认15分钟
                token_type = token_data.get('token_type', 'Bearer')
                
                print(f"成功获取令牌: {self.token[:10]}..." if self.token else "获取的令牌为空")
                print(f"令牌类型: {token_type}")
                
                if token_type.lower() != 'bearer':
                    print(f"警告: 获取到的令牌类型不是Bearer: {token_type}")
                
            except json.JSONDecodeError:
                # 如果不是JSON，尝试解析为XML
                try:
                    token_data = response.text
                    root = ET.fromstring(token_data)
                    self.token = root.find('accessToken').text
                    expires_in = int(root.find('expiresIn').text)
                    token_type = root.find('tokenType').text if root.find('tokenType') is not None else 'Bearer'
                    
                    print(f"成功获取令牌(XML格式): {self.token[:10]}..." if self.token else "获取的令牌为空")
                    print(f"令牌类型(XML): {token_type}")
                    
                except Exception as xml_error:
                    print(f"XML解析错误: {xml_error}")
                    raise Exception("无法解析认证响应，格式既不是JSON也不是XML")
            
            # 设置令牌过期时间（通常为15分钟）
            self.token_expiry = datetime.now() + timedelta(seconds=expires_in - 30)
            return self.token
            
        except Exception as e:
            traceback.print_exc()
            print(f"获取令牌失败: {e}")
            if 'response' in locals() and hasattr(response, 'text'):
                print(f"响应内容: {response.text}")
            raise
        
    def test_api_connection(self):
        """测试API连接有效性"""
        try:
            # 确保客户端ID和密钥不含任何空格或不可见字符
            client_id = self.client_id.strip()
            client_secret = self.client_secret.strip()
            
            # 确保没有额外的字符
            if not client_id or not client_secret:
                return False, "客户端ID或密钥为空"
            
            # 严格按照Walmart API格式要求创建认证凭据
            credentials = f"{client_id}:{client_secret}"
            
            # 使用标准的base64编码（注意编码前需要转换为bytes）
            encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('utf-8').strip()
            
            # 生成一个唯一的correlation_id
            correlation_id = datetime.now().strftime("%Y%m%dT%H%M%S%fZ")
            
            # 严格按照文档使用Basic认证 - 确保格式完全匹配："Authorization: Basic Base64Encode(clientId:clientSecret)"
            headers = {
                "Authorization": f"Basic {encoded_credentials}",
                "WM_SVC.NAME": "Walmart Marketplace",
                "WM_QOS.CORRELATION_ID": correlation_id,
                "Accept": "application/json",
                "Content-Type": "application/x-www-form-urlencoded"  # 确保使用正确的Content-Type
            }
            
            # 使用令牌端点进行测试连接
            url = f"{self.base_url}/token"
            
            print(f"测试API连接: {url}")
            print(f"Correlation ID: {correlation_id}")
            print(f"使用Basic认证头格式: Authorization: Basic [Base64Encoded]")
            # 打印原始的认证凭据（不包含敏感部分）
            masked_client_id = client_id[:5] + "****" if len(client_id) > 5 else "****"
            masked_client_secret = client_secret[:5] + "****" if len(client_secret) > 5 else "****"
            print(f"认证凭据格式: {masked_client_id}:{masked_client_secret}")
            
            # 使用标准OAuth2参数
            data = "grant_type=client_credentials"
            
            # 打印完整请求信息便于调试
            print(f"请求方法: POST")
            print(f"请求头: {json.dumps({k: v if k != 'Authorization' else 'Basic ***' for k, v in headers.items()})}")
            print(f"请求体: {data}")
            
            # 发送请求
            response = requests.post(url, headers=headers, data=data)
            
            print(f"测试响应状态码: {response.status_code}")
            
            # 仅打印响应部分内容（不包含敏感数据）
            if response.text:
                response_preview = response.text[:200]
                if len(response_preview) < len(response.text):
                    response_preview += "..."
                print(f"测试响应内容: {response_preview}")
            
            # 分析响应详情，提供更具体的错误信息
            if response.status_code == 200:
                return True, "API连接测试成功"
            elif response.status_code == 401:
                error_info = "未授权：检查客户端ID和密钥是否正确，确保账户具有API访问权限"
                try:
                    error_json = response.json()
                    if 'error' in error_json and len(error_json['error']) > 0:
                        error_details = error_json['error'][0]
                        if 'info' in error_details:
                            error_info = f"授权错误: {error_details['info']}"
                except Exception as parse_error:
                    print(f"解析错误响应时出错: {parse_error}")
                
                # 打印详细的诊断信息
                print("\n详细诊断信息:")
                print(f"1. 确保Client ID和Client Secret完全正确，无多余空格")
                print(f"2. 确保您的账户有API访问权限")
                print(f"3. 检查您的API环境是否正确 (测试环境/生产环境)")
                print(f"4. 认证头格式: Basic {encoded_credentials[:15]}...") 
                
                return False, error_info
            elif response.status_code == 403:
                return False, "API访问被拒绝：API密钥可能没有所需权限"
            else:
                error_info = f"API连接测试失败: HTTP {response.status_code}"
                try:
                    error_json = response.json()
                    if 'error' in error_json and len(error_json['error']) > 0:
                        error_details = error_json['error'][0]
                        if 'description' in error_details:
                            error_info = f"请求错误: {error_details['description']}"
                except Exception as parse_error:
                    print(f"解析错误响应时出错: {parse_error}")
                return False, error_info
                
        except Exception as e:
            traceback.print_exc()
            error_msg = f"API连接测试异常: {str(e)}"
            print(error_msg)
            return False, error_msg
    
    def upload_product_to_catalog(self, product_data):
        """上传单个产品到Walmart目录（通过Catalog Match API）"""
        token = self.get_token()
        
        if not token:
            raise Exception("未能获取有效的OAuth访问令牌")
        
        correlation_id = f"catalog-match-{time.time()}"
        
        # 使用Bearer令牌认证
        headers = {
            "Authorization": f"Bearer {token}",
            "WM_SVC.NAME": "Walmart Marketplace",
            "WM_QOS.CORRELATION_ID": correlation_id,
            "Accept": "application/json",
            "Content-Type": "application/json"
        }
        
        # 使用目录匹配API端点
        url = f"{self.base_url}/feeds?feedType=MP_ITEM_MATCH"
        
        try:
            print(f"上传产品请求URL: {url}")
            print(f"请求头: Authorization=Bearer {token[:10]}..., WM_SVC.NAME=Walmart Marketplace, WM_QOS.CORRELATION_ID={correlation_id}")
            
            response = requests.post(url, headers=headers, json=product_data)
            
            # 打印响应信息
            print(f"上传响应状态码: {response.status_code}")
            if response.text:
                print(f"上传响应内容: {response.text[:200]}...")
            
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"上传产品到目录失败: {e}")
            if 'response' in locals() and hasattr(response, 'text'):
                print(f"响应内容: {response.text}")
            raise
    
    def check_feed_status(self, feed_id):
        """检查上传任务的状态"""
        token = self.get_token()
        
        if not token:
            raise Exception("未能获取有效的OAuth访问令牌")
        
        # 生成一个唯一的correlation_id
        correlation_id = f"status-{datetime.now().strftime('%Y%m%dT%H%M%S%f')}"
        
        # 使用Bearer令牌认证
        headers = {
            "Authorization": f"Bearer {token}",
            "WM_SVC.NAME": "Walmart Marketplace",
            "WM_QOS.CORRELATION_ID": correlation_id,
            "Accept": "application/json"
        }
        
        url = f"{self.base_url}/feeds/{feed_id}"
        
        try:
            print(f"检查任务状态URL: {url}")
            print(f"请求头: Authorization=Bearer {token[:10]}..., WM_SVC.NAME=Walmart Marketplace, Accept=application/json")
            print(f"Correlation ID: {correlation_id}")
            
            response = requests.get(url, headers=headers)
            
            # 打印响应信息
            print(f"状态检查响应状态码: {response.status_code}")
            if response.text:
                print(f"状态检查响应内容: {response.text[:200]}...")
            
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"检查任务状态失败: {e}")
            if 'response' in locals() and hasattr(response, 'text'):
                print(f"响应内容: {response.text}")
            raise
            
    def bulk_upload_products(self, products_data):
        """批量上传多个产品（使用目录匹配API）"""
        token = self.get_token()
        
        if not token:
            raise Exception("未能获取有效的OAuth访问令牌")
            
        print(f"使用Bearer令牌进行批量上传: {token[:10]}...")
        
        # 生成唯一的相关ID，确保每次请求都有不同的ID
        correlation_id = f"catalog-match-{datetime.now().strftime('%Y%m%dT%H%M%S%f')}"
        
        # 使用Bearer令牌认证
        headers = {
            "Authorization": f"Bearer {token}",
            "WM_SVC.NAME": "Walmart Marketplace",
            "WM_QOS.CORRELATION_ID": correlation_id,
            "Accept": "application/json",
            "Content-Type": "application/json"
        }
        
        # 使用目录匹配API端点
        url = f"{self.base_url}/feeds?feedType=MP_ITEM_MATCH"
        
        # 按照Catalog Match API的格式
        feed_data = {
            "SupplierItems": {
                "Items": products_data
            }
        }
        
        try:
            # 打印请求信息以便调试
            print(f"批量上传请求URL: {url}")
            print(f"请求头: Authorization=Bearer {token[:10]}..., WM_SVC.NAME=Walmart Marketplace")
            print(f"Correlation ID: {correlation_id}")
            print(f"请求体(部分): {json.dumps(feed_data)[:200]}...")
            
            response = requests.post(url, headers=headers, json=feed_data)
            
            # 打印响应信息
            print(f"批量上传响应状态码: {response.status_code}")
            if response.text:
                print(f"批量上传响应内容: {response.text[:200]}...")
            
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"批量上传产品失败: {e}")
            if 'response' in locals() and hasattr(response, 'text'):
                print(f"响应内容: {response.text}")
            if 'response' in locals() and hasattr(response, 'status_code'):
                if response.status_code == 401:
                    print("授权错误：请检查您的API密钥或令牌是否有效，以及是否有适当的权限")
                    print("提示：确保您的账户有目录匹配API(MP_ITEM_MATCH)的访问权限")
                elif response.status_code == 400:
                    print("请求格式错误：请检查产品数据格式是否符合要求")
            raise
            
    def convert_to_catalog_match_format(self, product):
        """将产品数据转换为目录匹配API所需的格式"""
        # 从产品数据中提取必要的字段
        sku = product.get('sku')
        product_name = product.get('productName')
        price = product.get('price')
        inventory = product.get('inventory', {}).get('quantity')
        
        # 确保有必填字段
        if not sku or not product_name:
            raise ValueError("产品SKU和名称是必填项")
            
        # 提取图片
        images = product.get('images', [])
        
        # 从详情中提取产品ID信息
        product_details = product.get('productDetails', {})
        brand = product_details.get('brand', '')
        
        # 设置产品标识符 - 尝试从产品中提取UPC或GTIN等信息
        product_id = None
        product_id_type = None
        
        # 尝试从详情中获取产品ID
        for identifier in product.get('productIdentifiers', []):
            if identifier.get('productIdType') in ['UPC', 'GTIN', 'EAN', 'ISBN']:
                product_id_type = identifier.get('productIdType')
                product_id = identifier.get('productId')
                break
        
        # 如果没有找到产品ID，可能需要手动指定或报错
        if not product_id:
            raise ValueError(f"产品 {sku} 缺少有效的产品标识符(UPC/GTIN/EAN/ISBN)")
            
        # 构建目录匹配格式的数据
        catalog_match_item = {
            "sku": sku,
            "productIdentifiers": [
                {
                    "productIdType": product_id_type,
                    "productId": product_id
                }
            ],
            "price": price
        }
        
        # 添加重量信息
        if 'shippingWeight' in product:
            catalog_match_item["ShippingWeight"] = product.get('shippingWeight')
            
        # 添加条件信息（如果有）
        if 'condition' in product:
            catalog_match_item["condition"] = product.get('condition')
        
        return catalog_match_item

    def upload_single_product(self, sku, product_id, product_id_type, price, shipping_weight=None, condition="New"):
        """上传单个产品到沃尔玛目录（使用Catalog Match API，简化版）"""
        try:
            # 1. 构建产品数据
            product_data = {
                "sku": sku,
                "productIdentifiers": [
                    {
                        "productIdType": product_id_type,
                        "productId": product_id
                    }
                ],
                "price": price
            }
            
            # 添加可选字段
            if shipping_weight is not None:
                product_data["ShippingWeight"] = shipping_weight
                
            if condition is not None:
                product_data["condition"] = condition
            
            # 2. 获取令牌
            token = self.get_token()
            if not token:
                print("错误: 无法获取访问令牌")
                return False, "认证失败: 无法获取令牌"
            
            # 生成唯一的correlation_id
            correlation_id = datetime.now().strftime("%Y%m%dT%H%M%S%fZ")
            
            # 3. 构建请求头
            headers = {
                "Authorization": f"Bearer {token}",
                "WM_SVC.NAME": "Walmart Marketplace",
                "WM_QOS.CORRELATION_ID": correlation_id,
                "Accept": "application/json",
                "Content-Type": "application/json"
            }
            
            # 4. 构建请求数据
            request_data = {
                "SupplierItems": {
                    "Items": [product_data]  # 只包含一个产品
                }
            }
            
            # 5. 设置API端点
            url = f"{self.base_url}/feeds?feedType=MP_ITEM_MATCH"
            
            # 6. 打印请求信息
            print(f"\n开始上传单个产品...")
            print(f"SKU: {sku}")
            print(f"产品ID: {product_id} (类型: {product_id_type})")
            print(f"价格: {price}")
            if shipping_weight:
                print(f"重量: {shipping_weight}")
            print(f"Correlation ID: {correlation_id}")
            
            # 7. 发送请求
            response = requests.post(url, headers=headers, json=request_data)
            
            # 8. 打印响应信息
            print(f"响应状态码: {response.status_code}")
            if response.text:
                response_preview = response.text[:200]
                if len(response_preview) < len(response.text):
                    response_preview += "..."
                print(f"响应内容: {response_preview}")
            
            # 9. 处理响应
            if response.status_code == 200 or response.status_code == 201:
                result = response.json()
                feed_id = result.get('feedId')
                print(f"上传成功! Feed ID: {feed_id}")
                
                # 如果需要，可以立即检查状态
                print("等待5秒后检查上传状态...")
                time.sleep(5)
                try:
                    status = self.check_feed_status(feed_id)
                    feed_status = status.get('feedStatus', '未知')
                    print(f"当前上传状态: {feed_status}")
                    return True, {"feed_id": feed_id, "status": feed_status}
                except Exception as status_error:
                    print(f"检查上传状态失败: {status_error}")
                    return True, {"feed_id": feed_id, "status": "unknown"}
            else:
                # 处理错误
                error_message = "上传失败"
                try:
                    error_json = response.json()
                    if 'error' in error_json and len(error_json['error']) > 0:
                        error_details = error_json['error'][0]
                        if 'description' in error_details:
                            error_message = error_details['description']
                except Exception:
                    pass
                
                print(f"错误: {error_message}")
                return False, error_message
                
        except Exception as e:
            error_msg = f"上传单个产品时发生错误: {str(e)}"
            print(error_msg)
            traceback.print_exc()
            return False, error_msg 